package com.nsxy.common.config;

import java.beans.PropertyVetoException;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * JPA 配置
 * @author 潜行的青衣
 * @see
 */
@Order(30)
@Configuration
@PropertySource(value = {
	"classpath:jdbc.properties", 
	"classpath:hibernate.properties"
})
@EnableTransactionManagement
@EnableJpaRepositories("com.nsxy.**.dao")
@ComponentScan(basePackages = {"com.nsxy.**.dao", "com.nsxy.**.service"})
public class JPAConfig implements EnvironmentAware {

	private Environment env;
	
	@Bean
	public DataSource dataSource() throws PropertyVetoException {
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setDriverClass(env.getProperty("db.driver.class"));
		dataSource.setJdbcUrl(env.getProperty("db.url"));
		dataSource.setUser(env.getProperty("db.username"));
		dataSource.setPassword(env.getProperty("db.password"));
		
		dataSource.setAutoCommitOnClose(env.getProperty("db.autoCommitOnClose", Boolean.class));
		dataSource.setMinPoolSize(env.getProperty("db.minPoolSize", Integer.class));
		dataSource.setMaxPoolSize(env.getProperty("db.maxPoolSize", Integer.class));
		dataSource.setInitialPoolSize(env.getProperty("db.initialPoolSize", Integer.class));
		dataSource.setMaxIdleTime(env.getProperty("db.maxIdleTime", Integer.class));
		dataSource.setAcquireIncrement(env.getProperty("db.acquireIncrement", Integer.class));
		dataSource.setAcquireRetryAttempts(env.getProperty("db.acquireRetryAttempts", Integer.class));
		dataSource.setCheckoutTimeout(env.getProperty("db.checkoutTimeout", Integer.class));
		return dataSource;
	}
	
	@Bean
	public EntityManagerFactory entityManagerFactory() throws PropertyVetoException {
		HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
		vendorAdapter.setGenerateDdl(true);
		LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
		factory.setJpaDialect(new HibernateJpaDialect());
		factory.setPersistenceProviderClass(HibernatePersistenceProvider.class);
		factory.setPackagesToScan(env.getProperty("jpa.packages.to.scan"));
		factory.setJpaVendorAdapter(vendorAdapter);
		
		factory.setDataSource(dataSource());
		factory.setLoadTimeWeaver(instrumentationLoadTimeWeaver());
		factory.afterPropertiesSet();
		return factory.getObject();
	}

	@Bean
	public PlatformTransactionManager transactionManager() throws PropertyVetoException {
		JpaTransactionManager txManager = new JpaTransactionManager();
		txManager.setEntityManagerFactory(entityManagerFactory());
		txManager.afterPropertiesSet();
		return txManager;
	}

	@Bean
	public InstrumentationLoadTimeWeaver instrumentationLoadTimeWeaver() {
		return new InstrumentationLoadTimeWeaver();
	}
	
	@Bean
	public TransactionInterceptor transactionInterceptor() throws PropertyVetoException {
		TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
		transactionInterceptor.setTransactionManager(this.transactionManager());
		NameMatchTransactionAttributeSource transactionAttributeSource = new NameMatchTransactionAttributeSource();
		RuleBasedTransactionAttribute attr = new RuleBasedTransactionAttribute();
		List<RollbackRuleAttribute> rollbackRules = new ArrayList<RollbackRuleAttribute>();
		RollbackRuleAttribute rule = new RollbackRuleAttribute(Throwable.class);
		rollbackRules.add(rule);
		attr.setRollbackRules(rollbackRules);
		transactionAttributeSource.addTransactionalMethod("*", attr);
		transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
		transactionInterceptor.afterPropertiesSet();
		return transactionInterceptor;
	}
	
	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}
	
	@Bean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
		creator.setProxyTargetClass(true);
		return creator;
	}
	
	@Bean
	public DefaultPointcutAdvisor controllerPointcutAdvisor() throws PropertyVetoException {
		final TransactionInterceptor transactionInterceptor = transactionInterceptor();
		DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
		advisor.setAdvice(transactionInterceptor);
		AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
		pointcut.setExpression(env.getProperty("aop.controller.execution"));
		advisor.setPointcut(pointcut);
		advisor.setOrder(10);
		return advisor;
	}
	
	@Bean
	public DefaultPointcutAdvisor servicePointcutAdvisor() throws PropertyVetoException {
		DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
		advisor.setAdvice(transactionInterceptor());
		AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
		pointcut.setExpression(env.getProperty("aop.service.execution"));
		advisor.setPointcut(pointcut);
		advisor.setOrder(20);
		return advisor;
	}
	
	/* (non-Javadoc)
	 * @see org.springframework.context.EnvironmentAware#setEnvironment(org.springframework.core.env.Environment)
	 */
	@Override
	public void setEnvironment(Environment environment) {
		this.env = environment;
	}

}
